package com.qen.common;

import cn.hutool.core.collection.CollectionUtil;
import com.qen.interfaces.SFunction;
import com.qen.wrapper.support.ColumnCache;
import com.qen.wrapper.support.LambdaMeta;
import com.qen.wrapper.support.ReflectLambdaMeta;
import org.beetl.sql.clazz.ClassDesc;
import org.beetl.sql.clazz.kit.BeetlSQLException;
import org.beetl.sql.clazz.kit.StringKit;

import java.lang.invoke.SerializedLambda;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import static java.util.Locale.ENGLISH;

/**
 * Lambda 解析工具类
 *
 * @author licz
 * @since 1.0.0
 */
public final class LambdaUtils {

    /**
     * 字段映射
     */
    private static final Map<String, Map<String, ColumnCache>> COLUMN_CACHE_MAP = new ConcurrentHashMap<>();

    /**
     * 该缓存可能会在任意不定的时间被清除
     * TODO 遇上再改
     *
     * @param func 需要解析的 lambda 对象
     * @param <T>  类型，被调用的 Function 对象的目标类型
     * @return 返回解析后的结果
     */
    public static <T> LambdaMeta extract(SFunction<T, ?> func) {
        // 1. IDEA 调试模式下 lambda 表达式是一个代理
//        if (func instanceof Proxy) {
//            return new IdeaProxyLambdaMeta((Proxy) func);
//        }
        // 2. 反射读取
        try {
            Method method = func.getClass().getDeclaredMethod("writeReplace");
            method.setAccessible(true);
            return new ReflectLambdaMeta((SerializedLambda) method.invoke(func), func.getClass().getClassLoader());
        } catch (Throwable e) {
            // 3. 反射失败使用序列化的方式读取
            throw new BeetlSQLException(BeetlSQLException.GET_OBJECT_PROPERTY_ERROR, e);
        }
    }

    /**
     * 格式化 key 将传入的 key 变更为大写格式
     * 为了支持首字母是大写的字段
     *
     * <pre>
     *     Assert.assertEquals("USERID", formatKey("userId"))
     * </pre>
     *
     * @param key key
     * @return 大写的 key
     */
    public static String formatKey(String key) {
        return key.toUpperCase(ENGLISH);
    }

    /**
     * 将传入的表信息加入缓存
     *
     * @param tableInfo 表信息
     */
    public static void installCache(ClassDesc tableInfo) {
        COLUMN_CACHE_MAP.put(tableInfo.getTargetClass().getSimpleName(), createColumnCacheMap(tableInfo));
    }

    /**
     * 缓存实体字段 MAP 信息
     *
     * @param info 表信息
     * @return 缓存 map
     */
    private static Map<String, ColumnCache> createColumnCacheMap(ClassDesc info) {
        Map<String, ColumnCache> map = new HashMap<>(info.getInCols().size());
        if (CollectionUtil.isNotEmpty(info.getIdAttrs())) {
            List<String> ids = info.getIdAttrs();
            for (int i = 0; i < ids.size(); i++) {
                ColumnCache c = new ColumnCache(info.getIdCols().get(i));
                map.put(formatKey(ids.get(i)), c);
            }
        }
        info.getInCols().forEach(i ->
                map.put(formatKey(StringKit.deCodeUnderlined(i)), new ColumnCache(i))
        );
//        info.getAttrs().forEach(i ->
//                map.put(formatKey(i), new ColumnCache(i))
//        );
        return map;
    }

    /**
     * 获取实体对应字段 MAP
     *
     * @param clazz 实体类
     * @return 缓存 map
     */
    public static Map<String, ColumnCache> getColumnMap(Class<?> clazz) {
        return COLUMN_CACHE_MAP.computeIfAbsent(clazz.getName(), key -> {
//            ClassDesc info = TableInfoHelper.getClassInfo(clazz);
            ClassDesc info = BeetlUtils.getSQLManager().getClassDesc(clazz);
            return info == null ? null : createColumnCacheMap(info);
        });
    }

    public static <T> String getName(SFunction<T, ?> fn) {
        LambdaMeta extract = extract(fn);
        return PropertyNamer.methodToProperty(extract.getImplMethodName());
    }


    @SuppressWarnings("unchecked")
    public static <T> Class<T> getEntityClass(SFunction<T, ?> fn) {
        return (Class<T>) extract(fn).getInstantiatedClass();
    }

    public static <T> LambdaMeta getMeta(SFunction<T, ?> fn) {
        return extract(fn);
    }

}
